<?php

namespace control;

require_once 'interface.php';

/**
 * 组件类
 * Class componentClass
 */
class componentClass extends adIndexClass
{
    private const page = 'component';
    private const title = '组件类';
    public static function spanLabel($title, $value): string
    {
        return <<<eof
<span style="margin-left: 20px;font-weight: bold">{$title}：$value</span>
eof;

    }
    /**
     * 模糊查询框
     * @param $data
     * @return string
     */
    public static function selectText($data): string
    {
        $clickId = self::getId();
        $data['name'] = $data['name'] ?? 'hiddenId';
        $data['value'] = $data['value'] ?? '';
        $data['title'] = !empty($data['title'] ?? '') ? $data['title'] : '-请选择-';
        $data['cla'] = $data['cla'] ?? '';
        $data['fun'] = $data['fun'] ?? '';
        $data['width'] = $data['width'] ?? 0;
        $data['change'] = (!empty($data['change'] ?? '')) ? $data['change'].'(data)' : '';
        $data['class'] = $data['class'] ?? '';
        $data['param'] = $data['param'] ?? '{}';
        $data['attach'] = $data['attach'] ?? '';
        $hidden = hidden(array('name' => $data['name'], 'value' => $data['value']));
        return
<<<eof
<div class="autocomplete-select {$data['class']}" data-name="{$data['name']}" id="{$clickId}">
    <div class="autocomplete-label-wrap">
        <div class="autocomplete-label">{$data['title']}</div>
        <span class="autocomplete-arrow"></span>
    </div>
    <div class="hide">
        {$hidden}
    </div>
</div>
<script>
$("#{$clickId}").searchAutocomplete({
    width:'$data[width]',
    param:{$data['param']},
    attach:`{$data['attach']}`,
    url:root + 'control/adIndex.html?cla={$data['cla']}&fun={$data['fun']}',
    callback:function(data,e){
       let name = $(e).data('name');
       e.find("input[name='"+name+"']").val(data.id);
       e.find('.autocomplete-label').text(data.title);
       data.clickId = $("#{$clickId}")
       {$data['change']}
}}) 
</script>
eof;
    }

    /**
     * 跳转
     * @param $text
     * @param $url
     * @param $urlTitle
     * @param string $class
     * @return string
     */
    public static function urlTitle($text, $url, $urlTitle, $class = ''): string
    {
        if (empty($text)) {
            return '';
        }
        $class = $class ?: 'del-row-button';
        return "<span class='$class' onclick=\"newIframe('{$urlTitle}','{$url}')\">{$text}</span>";
    }

    /**
     * 科目选择器
     */
    public static function subjectSelect($subjectId = '', $isShowParent = true): string
    {
        return self::subjectComponent(['name' => 'subjectId', 'inputName' => 'subjectName', 'value' => $subjectId, 'isShowParent' => $isShowParent]);
    }

    /**
     *自定义名称的科目选择器
     */
    public static function customSubject($data): string
    {
        return self::subjectComponent($data);
    }

    public static function subjectComponent($data)
    {
        $data['name'] = $data['name'] ?? 'subjectId';
        $data['inputName'] = $data['inputName'] ?? suiji();
        $data['value'] = $data['value'] ?? '';
        $data['title'] = $data['title'] ?? '科目';
        $data['icon'] = $data['icon'] ?? false;
        $isShowParent = $data['isShowParent'] ?? true;
        $icon = "hide";
        if ($data['icon']){
            $icon = '';
        }
        $subject = query('subject', "id = '$data[value]'");
        $selectorId = suiji();
        $html = "<span style='position: relative' class='subjectSelectorSpan'>" . text(array('name' => $data['inputName'], 'data' => ['name' => $data['name']], 'class' => 'subjectSelector', 'id' => $selectorId, 'title' => $data['title'], 'value' => $subject['code'] . $subject['name'])) . hidden(array('name' => $data['name'], 'value' => $subject['id'])) . "<img class='$icon' onclick='chooseSubject(this,\"{$data['inputName']}\",\"{$isShowParent}\")' style='position:absolute;right:10px;top:1px;cursor: pointer;' src='" . root . "img/control/find.svg?v=2'/></span>";
        $html .= <<<eof

<script>

$("#{$selectorId}").bigAutocomplete({
    width:200,
    url:root + 'control/adIndex.html?cla=component&fun=getSubject',
    param:{showParent:'$isShowParent'},
    callback:function(data,e){
       e.siblings("input[name='{$data['name']}']").val(data.id)
}}) 
</script>
<style>
.subjectSelector{padding-right: 34px !important;width: 177px !important;}
.pageMenu{margin: 10px 0 0;}
.pageMenu > ul {
    padding: 0 20px;
}
.pageMenu > ul > li {
    float: left;
    padding: 6px 10px;
    background-color: rgb(255, 255, 255);
    cursor: pointer;
}
.subject-ul>li{padding: 2px 10px;cursor: pointer}
.subject-ul>li:hover{background-color: rgb(242, 242, 242)}
.subject-div{   
    height: 450px;
    margin: 5px 0 5px 0;
    border-radius: 2px;
    overflow: auto;
    }
.confirm{
  width: 100px;
}
.cancel,.cancel:hover{
        width: 100px;
        background-color: white;
        color: #7F7F7F;
        border: 1px solid rgba(215, 215, 215, 1);
        margin-right: 5px;
}
</style>
eof;
        return $html;
    }

    /**
     * 模糊查询科目
     */
    public function getSubject(): bool|string
    {
        $val = $this->post('keyword');//关键词
        $showParent = (bool)$this->post('showParent');
        $json['data'] = [];
        $where = " and coid='" . self::$coid . "' and state = '启用'";
        if ($val) {
            $where .= " and (name like '%$val%' or code like '%$val%' or concat(code,' ',name) like '%$val%')";
        }
        $all = find(array('table' => 'subject', 'where' => "1=1 $where order by code asc"));
        $resArr =self::subjectSort($all['array'],$showParent);
        foreach ($resArr as $array) {
            $json['data'][] = array('title' => $array['code'] . ' ' . $array['name'], 'direction' => $array['direction'], 'id' => $array['id']);
        }
        $json['warn'] = 2;
        return json_encode($json, 320);
    }
    /**
     * 科目排序
     * @param $array
     * @param $showParent
     * @return array
     */
    public static function subjectSort($array,$showParent): array
    {
        $sortArray = [];
        $pidArray = [];
        $idArray = array_column($array,'id');
        foreach ($array as $one){
            if ($one['pid'] =='0' || !in_array($one['pid'],$idArray)){
                $sortArray[] = $one;
            }else{
                $pidArray[$one['pid']][] = $one;
            }
        }
        $backArray = [];
        foreach ($sortArray as $one) {
            $backArray = array_merge($backArray, self::recursiveSort($one, $pidArray, $showParent));
        }
        return $backArray;
    }

    /**
     * 循环排序
     * @param $item
     * @param $pidArray
     * @param $showParent
     * @return array
     */
    private static function recursiveSort($item, $pidArray, $showParent): array
    {
        $sortedArray = [];
        if (isset($pidArray[$item['id']])) {
            if ($showParent) {
                $sortedArray[] = $item;
            }
            foreach ($pidArray[$item['id']] as $subItem) {
                $sortedArray = array_merge($sortedArray, self::recursiveSort($subItem, $pidArray, $showParent));
            }
        } else {
            $sortedArray[] = $item;
        }
        return $sortedArray;
    }


    public function loopSubject($id, $where = '', $isShowParent = false)
    {
        $tr = [];
        if ($isShowParent) {
            $tr[] = $id;
        }
        //判断是否有下级分类
        $levelTwoRes = find(array('table' => 'subject', 'where' => "pid = '$id' and coid = '" . self::$coid . "' $where order by code"));
        foreach ($levelTwoRes['array'] as $val) {
            $res = $this->loopSubject($val['id'], $where, $isShowParent);
            $tr = array_merge($tr, $res);
        }
        if (count($levelTwoRes['array']) ==0 && !$isShowParent){
            $tr[] = $id;
        }
        return $tr;
    }

    public function subject($workFlow = '资产'): bool|string
    {
        $isShow = $this->post('isShow');
        $rowId = $this->post('rowId');
        $data = [
            "id" => $isShow,
            "menu" => subjectClass::enterpriseSubjectTab()
        ];
        $data['html'] = $this->subjectContent($workFlow, $isShow);

        $html = $this->tabRewrite($data) . "<div style='padding: 0 20px'><input name='chooseId' value='' type='hidden'><input name='chooseName' value='' type='hidden'></div>";
        $button = "<input type='button' class='button right cancel' onclick=\"$('#componentSubject').hide()\" value='取消'>&emsp;
<input type='button' onclick=\"confirmBtn('{$rowId}')\" class='button right confirm' value='确认'>";
        return $this->layer(array("title" => "科目选择器", "height" => 600, "width" => 600, "zindex" => 12, "html" => $html, "button" => $button));
    }

    public function subjectContent($type, $isShow): string
    {
        $type = $type . '类';
        $all = find(array('table' => 'subject', 'where' => "coid = '" . self::$coid . "'  and type = '$type' order by list asc"));
        $resArr =self::subjectSort($all['array'],$isShow);
        $li = '';
        foreach ($resArr as $array) {
            $subject = $array;
            if (!$isShow) {
                $subject['name'] = subjectClass::getName($subject['id']);
            }
            $empty = '';
            if ($isShow) {
                if ($subject['level'] > 1) {
                    for ($i = 1; $i < $subject['level']; $i++) {
                        $empty .= "&emsp;";
                    }
                }
            }
            //需要新增按钮且有下级的，不能选择有下级的科目，只能选最下级的
            $onclick = "onclick='chooseSubjectId(this)'";
            if (self::total(['table'=>'subject','where'=>"pid = '$subject[id]'"]) >0){
                $onclick = "";
            }
            $li .= "<li data-id='{$subject['id']}' data-name='{$subject['code']}{$subject['name']}' $onclick>$empty{$subject['code']}&nbsp;{$subject['name']}</li>";
        }
        return <<<eof
                <div class="subject-div">
                    <ul class="subject-ul">
                    $li
                </ul>
                </div>
eof;

    }

    public function debt()
    {
        $id = $this->get('id');
        if ($id =='voucherEditMode'){
            $json['html'] = $this->subjectContent('负债', true);
        }else{
            $json['html'] = $this->subjectContent('负债', $this->get('id'));
        }
        return json_encode($json, 320);
    }

    public function interests()
    {
        $id = $this->get('id');
        if ($id =='voucherEditMode'){
            $json['html'] = $this->subjectContent('权益', true);
        }else{
            $json['html'] = $this->subjectContent('权益', $this->get('id'));
        }
        return json_encode($json, 320);
    }

    public function cost()
    {
        $id = $this->get('id');
        if ($id =='voucherEditMode'){
            $json['html'] = $this->subjectContent('成本', true);
        }else{
            $json['html'] = $this->subjectContent('成本', $this->get('id'));
        }
        return json_encode($json, 320);
    }

    public function loss()
    {
        $id = $this->get('id');
        if ($id =='voucherEditMode'){
            $json['html'] = $this->subjectContent('损益', true);
        }else{
            $json['html'] = $this->subjectContent('损益', $this->get('id'));
        }

        return json_encode($json, 320);
    }

    public function common()
    {
        $id = $this->get('id');
        if ($id =='voucherEditMode'){
            $json['html'] = $this->subjectContent('共同', true);
        }else{
            $json['html'] = $this->subjectContent('共同', $this->get('id'));
        }
        return json_encode($json, 320);
    }

    public function tabRewrite(array $data): string
    {
        $html = $form = "";
        //生成导航
        if (isset($data['menu']) && $data['menu']) {
            $li = "";
            foreach ($data['menu'] as $key => $value) {
                if (empty($li)) {
                    $hover = " class='pageMenuHover' ";
                } else {
                    $hover = "";
                }
                if (is_array($value)) {
                    $tabName = $value['name'];//页卡中文名称
                    $className = $value['cla']; //调用的类的名称
                    $fun = $value['fun']; //调用的方法的名称
                } else {
                    $tabName = $value;
                    $className = self::page;
                    $fun = $key;
                }
                $li .= "<li liMenu='" . $fun . "' onclick=\"liTab('" . $className . "','" . $fun . "','" . $data['id'] . "')\" " . $hover . ">" . $tabName . "</li>";
                //如果GET menu为当前页卡
                if ($this->get('menu') == $fun) {
                    $html .= "<script>$(function(){liTab('" . $className . "','" . $fun . "','" . $data['id'] . "')});</script>";
                }
            }
            $html .= "
			<div id='liMenu' class='pageMenu'>
				<ul>" . $li . "</ul>
                <div class='space bg-light-grey'></div>
				<div class='clear'></div>
			</div>
			";
        }

        //第一个页卡的关键词
        if (empty($data['menu'])) {
            $pageDiv = $this->get('fun');
        } else {
            $pageDiv = key($data['menu']);
        }
        //生成页卡
        $html .= "
		<div class='' page_content='" . $pageDiv . "'>
			" . $form . ($data['html'] ?? null) . clear . "
		</div>
		";
        //返回
        return $html;
    }


    /**
     * 提示组件
     * @param string $text 提示文字
     * @param string $Symbol 提示符号
     * @param bool $allowHTML 内容是否是HTMl
     * @param string $class
     * @param string $label
     * @return string
     */
    public static function tipComponent(string $text, string $Symbol = 'i', $allowHTML = false,$class='',$label='div'): string
    {
        $suiji = suiji();
        return <<<efo
        <$label id="$suiji" class="tip-component $class">{$Symbol}</$label>
<script>
//开源地址https://github.com/atomiks/tippyjs
tippy("#$suiji", {
     content: "{$text}",
     interactive: true,
     allowHTML: "$allowHTML"
})
</script>
efo;
    }

    /**
     * 提示组件
     * @param string $text 提示文字
     * @param string $class
     * @return string
     */
    public static function promptSvg(string $text, $class='width20'): string
    {
        $root = root;
        return <<<efo
        <img src="{$root}img/svg/tip.svg" title="$text" class="$class">
efo;
    }

    /**
     * 显示文件
     */
    public function fileDetail(): bool|string
    {
        $id = $this->post('id');
        $type = $this->post('type');
        //生成表单
        $html = $this->layerEdit(array("table" => array(
            [
                "附件：" => fileUploadClass::showFile($id, $type),
            ],
        ),
        ));
        //返回
        return $this->layer(array("title" => "文件", "height" => 400, "width" => 900, "html" => $html, "button" => false));

    }

    /*操作记录*/
    public function operateList()
    {
        $result = find(array('table' => 'auditing_follow', 'where' => "typeId = '".$this->post('id')."' order by time desc"));
        $html     = '';
        $sign = $this->post('sign');
        if (is_array($result['array'])) {
            foreach ($result['array'] as $array) {
                if($sign == 1) {
                    $text = "【{$array['staffName']}】于{$array['time']}《{$array['text']}》了" . $array['type'];
                }else{
                    $text = $array['text']. '&emsp;' .$array['time'];
                }
                $html .=
                    "<div class='record'>".$text."</div>"
                ;
            }
        }
        if(empty($html)) {
            $html .= "
                <div class='no-data-div'>
                    <img class='no-data-img' src='{$this->root}img/svg/noData.svg' >
                    <span class='no-data-text'>暂无数据</span>
                </div>
            ";
        }
        //返回弹出层
        return $this->layer(array("title" => '操作记录', "height" => 400, "width" => 500, "html" => $html, "zindex" => 100, "button" => false));
    }


    /**
     * 图片展示
     * @param string $img 图片路径
     * @param int $width 宽
     * @param int $height 高
     * @return string
     */
    public static function thumbnail(string $img, $width = 100, $height = 100): string
    {
        if (empty($img)) {
            $html = "<img class='smallImg imgHover' style='width:{$width}px;height:{$height}px' src='" . root . "img/control/img.svg'>\n";
        } else {
            if (strstr($img, "http") !== false) {
                $r = "";
            } else {
                $r = root;
            }
            $url = $r . $img;
            $html = "<a target='_blank' href='$url' title='点击查看大图'><img alt='' class='smallImg imgHover'  style='width:{$width}px;height:{$height}px' src='{$url}'></a>\n";
        }
        return $html;
    }

    /**
     * 标签编辑组件
     */
    public static function singleInput($data): string
    {
        $label = "<ul class='label-edit-ul addAttr'>";
        foreach ($data['list'] as $val) {
            if (is_array($val)) {
                $label .= "<li><span class='background-color-gray li-ico'>{$val['name']}<input name='labelId[]' type='hidden' value='{$val['id']}'><input name='label_name[]' type='hidden' value='{$val['name']}'></span><a onclick='del_this(this)' href='javascript:void(0)'><div><span class='u-icon-delete'><i></i></span></div></a></li>";
            } elseif (!empty($val)) {
                $label .= "<li><span class='background-color-gray li-ico'>{$val}<input name='label_name[]' type='hidden' value='{$val}'></span><a onclick='del_this(this)' href='javascript:void(0)'><div><span class='u-icon-delete'><i></i></span></div></a></li>";
            }
        }
        $label .= "<li><div class='add-attr-button' onclick=\"showInput(this)\">+</div></li></ul>";
        return $label;
    }

    /**
     * 显示输入框
     */
    public function showInput(): bool|string
    {
        $json['html'] = <<<efo
<li><input name="label_name[]" type="text" onblur="showAddButton(this)" value=""></li>
efo;
        return json_encode($json, 320);
    }

    /**
     * 显示添加按钮
     * @return bool|string
     */
    public function showAddButton(): bool|string
    {
        $val = $this->post('val');//值
        $json['html'] = '';
        if (!empty($val)) {
            $mxId = self::getId();
            $json['html'] = <<<efo
<li><span class='background-color-gray li-ico'>$val<input name='labelId[]' type='hidden' value='$mxId'><input name='label_name[]' type='hidden' value='$val'></span><a onclick='del_this(this)'><div><span class='u-icon-delete'><i></i></span></div></a></li>
efo;
        }
        return json_encode($json, 320);
    }


    /**
     * 显示员工
     * @param $selector
     * @param false $isEdit
     * @param string $boxid
     * @return string
     */
    public static function showSelector($selector, $isEdit = false, $boxid = 'staff-box'): string
    {
        $html = '';
        if (empty($selector) || is_array($selector)) {
            return $html;
        }
        $selector = explode(';', $selector);
        foreach ($selector as $val) {
            $department = query('department', "id = '$val'");
            if ($department['id']) {
                $name = $department['name'];
                $id = $department['id'];
            } else {
                $job = query('job', "id = '$val'");
                if ($job['id']) {
                    $name = $job['name'];
                    $id = $job['id'];
                } else {
                    $staff = query('staff', "stid = '$val'");
                    $name = $staff['name'];
                    $id = $staff['stid'];
                }
            }
            if ($id) {
                $del = '';
                if ($isEdit) {
                    $del = <<<eof
<a data-stid="{$id}" data-boxid="{$boxid}" onclick='del_staff(this)' href='javascript:void(0)'><div><span class='u-icon-delete'><i></i></span></div></a>
eof;
                }
                $html .= <<<eof
<span class="choose-part-value li-ico">{$name}</span>{$del}
eof;
            }
        }
        return $html;
    }

    /**
     *根据员工选择器选择的值获取所有员工ID
     * @param $selectorStaff
     * @return array
     */
    public static function returnStaffId($selectorStaff): array
    {
        $staff = [];
        if (empty($selectorStaff)) {
            return $staff;
        }
        $selector = explode(';', $selectorStaff);
        foreach ($selector as $value) {
            if (static::total(array('table' => 'staff', 'where' => "stid = '$value' and state !='离职'")) > 0) {
                $staff[] = $value;
            }
            $staff = array_unique(array_filter(array_merge($staff, self::departmentToStaff($value), self::jobToStaff($value))));
        }
        return $staff;
    }

    /**
     * 获取当前部门下的所有员工
     */
    public static function departmentToStaff($departmentId): array
    {
        $staff = [];
        if (empty($departmentId)) {
            return $staff;
        }
        $departmentAll = find(array('table' => 'department', 'where' => "pid = '$departmentId'"));
        foreach ($departmentAll['array'] as $department) {
            $staff = array_merge($staff, self::departmentToStaff($department['id']));
        }
        $all = find(array('table' => "staff as a join job as b on a.jobId = b.id", 'where' => "b.departmentId = '$departmentId' and a.state !='离职' and a.workFlow = '已复核' order by a.time asc", 'field' => "a.stid"));
        foreach ($all['array'] as $array) {
            $staff[] = $array['stid'];
        }
        return array_unique($staff);
    }

    /**
     * 获取当前职位下的所有员工
     */
    public static function jobToStaff($jobId): array
    {
        $staff = [];
        if (empty($jobId)) {
            return $staff;
        }
        $all = find(array('table' => "staff", 'where' => "jobId = '$jobId' and state !='离职' order by time asc", 'field' => "stid"));
        foreach ($all['array'] as $array) {
            $staff[] = $array['stid'];
        }
        return array_unique($staff);
    }


    /**
     * 员工选择器
     * @param $value
     * @param string $limit 限制选择范围 job:职位/staff:员工/空表示不限
     * @param string $name 隐藏于name
     * @return string
     */
    public static function staffSelector($value, $limit = '', $name = 'staff'): string
    {
        if ($limit == 'job') {
            $idName = 'setPosition';
        } elseif ($limit == 'staff') {
            $idName = 'staffSel';
        } else {
            $idName = 'staffSelector';
        }
        $boxId = $name . '-box';
        $html = self::showSelector($value, true, $boxId);
        return <<<eof
                    <div id="{$boxId}" data-box="$boxId">                       
                        <div class="js-party-select-result-list addAttr" style="display: inline-block;">{$html}</div>
                         <span class="spanButton staffSel" data-name="{$name}" id="$idName" style="display: inline-block;">选择</span>
                        <input id="{$boxId}layer" type="hidden" name="{$name}[]" value="{$value}">
                    </div>
eof;

    }

    /**
     * 物料选择器
     * @param $value
     * @param string $limit 限制选择范围 job:职位/staff:员工/空表示不限
     * @return string
     */
    public static function materialSelector($value = '', $limit = ''): string
    {
        $idName = 'materialSelector';
        $html = '';
        return <<<eof
                    <div id="staff-box">                       
                        <div class="textarea js-party-select-result-list addAttr" style="display: inline-block;border: 1px solid #d7d7d7">{$html}</div>
                         <span class="spanButton staffSel" id="$idName" style="display: inline-block;vertical-align: top">选择</span>
                        <input id="materialIds" type="hidden" name="materialIds[]" value="{$value}">
                    </div>
eof;

    }

    /**
     * 地址图标
     */
    public static function addressIco($click = ''): string
    {
        $root = root;
        return <<<efo
<img alt="" title="查看地图" src="{$root}img/control/location.svg" onclick="$click" style="cursor: pointer;width: 22px;height: 22px">
efo;

    }

    /**
     * 获取员工
     * @return bool|string
     */
    public function getStaff(): bool|string
    {
        $val = $this->post('keyword');//关键词
        $json['data'] = [];
        $where = "workFlow = '已复核'";
        if ($val) {
            $where .= " and (name like '%$val%' or orderNo like '%$val%')";
        }
        $res = find(array('table' => 'staff', 'where' => $where . " order by time asc limit 50"));
        foreach ($res['array'] as $val) {
            $json['data'][] = array('title' => $val['name'], 'id' => $val['stid']);
        }
        $json['warn'] = 2;
        return json_encode($json, 320);
    }

    /**
     * 模糊查询地址
     */
    public function getAddress(): bool|string
    {
        $val = $this->post('keyword');//关键词
        $json['warn'] = 0;
        if (!empty($val)) {
            $key = para('tencentMapKey');
            $res = self::curlGet("https://apis.map.qq.com/ws/place/v1/suggestion/?keyword={$val}&key={$key}&region=重庆", timeout: 60);//region=重庆&
            $res = json_decode($res, true);
            $json['data'] = [];
            if ($res['status'] == 0) {
                foreach ($res['data'] as $val) {
                    $json['data'][] = array('title' => $val['address'], 'province' => $val['province'], 'city' => $val['city'], 'lat' => $val['location']['lat'],
                        'lng' => $val['location']['lng'], 'location' => $val['location']['lat'] . ',' . $val['location']['lng']);
                }
                $json['warn'] = 2;
            } else {
                $json['warn'] = '请求数据失败：' . json_encode($res, 320);
            }
        }
        return json_encode($json, 320);
    }

    /**
     * 根据坐标解析出地址
     * @param $lat
     * @param $lng
     * @return array
     */
    public static function coordinateToAddr($lat, $lng): array
    {
        $key = para('tencentMapKey');
        $res = self::curlGet("https://apis.map.qq.com/ws/geocoder/v1/?location=" . $lat . ',' . $lng . "&key={$key}");
        $res = json_decode($res, true);

        $json['data'] = [];
        $json['warn'] = 'ok';
        if ($res['status'] == 0) {
            $json['data'] = $res['result'];
            $json['err_code'] = 0;
        } else {
            $json['err_code'] = 1;
            $json['warn'] = '请求数据失败：' . json_encode($res, 320);
        }
        return $json;
    }

    /**
     * 根据地址解析出坐标
     * @param $address
     * @return array
     */
    public static function addrToCoordinate($address): array
    {
        $key = para('tencentMapKey');
        $res = self::curlGet("https://apis.map.qq.com/ws/geocoder/v1/?address=" . $address . "&key={$key}");
        $res = json_decode($res, true);
        $json['data'] = [];
        $json['warn'] = 'ok';
        if ($res['status'] == 0) {
            $json['data'] = $res['result'];
            $json['err_code'] = 0;
        } else {
            $json['err_code'] = 1;
            $json['warn'] = '请求数据失败：' . json_encode($res, 320);
        }
        return $json;
    }


    public function getMaterialInfo(): bool|string
    {
        $res['data'] = self::getMaterialArray();
        return json_encode($res, 320);
    }


    /**
     * 获取员工数据
     *
     * @return array
     */
    public static function getMaterialArray($where = ''): array
    {
        $where = $where ?: "pid = '0' and coid = '" . self::$coid . "'";
        $where .= ' ORDER BY list ASC';
        $data = [];
        $department = find(['table' => 'material_type', 'field' => 'id, name as deptname', 'where' => $where]);
        foreach ($department['array'] as $value) {
            $value['type'] = 'materialType';
            $value['icon'] = root . 'img/control/department.svg';
            // 获取当前部门的职位
            $value['children'] = self::recursive('material', 'id,concat(code,name,model) as deptname', " typeId = '{$value['id']}' and coid = '" . self::$coid . "' and state = '启用'");
            // 判断是否有子部门
            $where = " pid = '{$value['id']}' and coid = '" . self::$coid . "'";
            if (sqlNum('material_type', $where) > 0) {
                $value['children'] = array_merge($value['children'], self::getMaterialArray($where));
            }
            $data[] = $value;
        }
        return $data;
    }

    /**
     * 查询
     */
    public static function recursive($table, $field, $where = ""): array
    {
        $data = [];
        $job = find(['table' => $table, 'field' => $field, 'where' => $where]);

        foreach ($job['array'] as $value) {
            $value['type'] = $table;
            $value['icon'] = root . 'img/control/staff.svg';
            $value['children'] = [];
            $data[] = $value;
        }
        return $data;
    }


    /**
     * 日期->xxxx-x
     * 季度选择器
     */
    public static function quarter_date_format($date)
    {
        if (empty($date)) {
            return '';
        }
        $month = (int)date('m', strtotime($date));
        $quarter = match ($month) {
            4, 5, 6 => 2,
            7, 8, 9 => 3,
            10, 11, 12 => 4,
            default => 1,
        };
        return date('Y', strtotime($date)) . '年' . $quarter . '季度';
    }

    /**
     * 文字转月份
     */
    public static function textToQuater($text)
    {
        if (empty($text)) {
            return '';
        }
        $year = mb_substr($text, 0, 4, 'utf-8');
        $quarter = (int)mb_substr($text, 6, 1, 'utf-8');
        return $year . '-' . $quarter;
    }

    /**
     * 文字转月份
     */
    public static function textToMonth($text)
    {
        if (empty($text)) {
            return '';
        }
        $year = mb_substr($text, 0, 4, 'utf-8');
        $quarter = (int)mb_substr($text, 5, 1, 'utf-8');
        $month = match ($quarter) {
            2 => '04',
            3 => '07',
            4 => 10,
            default => '01',
        };
        return $year . '-' . $month;
    }

    /**
     * 季度选择器
     * @param $data
     * @return string
     */
    public static function quarterSelect($data)
    {
        $data['name'] = $data['name'] ?? 'quarter';
        $data['id'] = $data['id'] ?? suiji();
        $data['class'] = $data['class'] ?? '';
        $data['title'] = $data['title'] ?? '';
        $data['value'] = $data['value'] ?? '';
        $data['startDate'] = self::textToQuater($data['value']);
        return <<<eof
<input name="{$data['name']}" type="text" class="{$data['class']}" value="{$data['value']}" startDate="{$data['startDate']}" placeholder="{$data['title']}" id="{$data['id']}">
    <script>
        //季节选择
        renderSeasonDate(document.getElementById('{$data['id']}'), 1);
        //联动测试
        initDateForm(1);
        /** 
         * 季度初始化 
         * @param ohd 季度input dom对象非jquery对象 
         * @param sgl 有值单个，无值默认范围
         */
        function renderSeasonDate(ohd, sgl) {
            var ele = $(ohd);
            laydate.render({
                elem: ohd,
                type: 'month',
                format: 'yyyy年M季度',
                range: sgl ? null : '~',
                min: "1900-1-1",
                max: "2099-12-31",
                btns: ['clear', 'confirm'],
                ready: function (value, date, endDate) {
                    var hd = $("#layui-laydate" + ele.attr("lay-key"));
                    if (hd.length > 0) {
                        hd.click(function () {
                            ren($(this));
                        });
                    }
                    ren(hd);
                },
                done: function (value, date, endDate) {
                    console.log(value)
                    console.log(date)
                    console.log(endDate)
                    if (!isNull(date) && date.month > 0 && date.month < 5) {
                        ele.attr("startDate", date.year + "-" + date.month);
                        
                    } else {
                        ele.attr("startDate", "");
                    }
                    if (!isNull(endDate) && endDate.month > 0 && endDate.month < 5) {
                        ele.attr("endDate", endDate.year + "-" + endDate.month)
                    } else {
                        ele.attr("endDate", "");
                    }
                }
            });
            var ren = function (thiz) {
                var mls = thiz.find(".laydate-month-list");
                mls.each(function (i, e) {
                    $(this).find("li").each(function (inx, ele) {
                        var cx = ele.innerHTML;
                        if (inx < 4) {
                            ele.innerHTML = cx.replace(/月/g, "季度");
                        } else {
                            ele.style.display = "none";
                        }
                    });
                });
            }
        }
        /**  
         * 初始化表单中年季月日范围选择  
         * 下拉框与日期框对应：  
         *  <select class="dateSelector" date-target="PI" ...  
         *  <input type="text" class="dateTargetPI" readonly ...  
         *  PI为自定义的对应关键字  
         *   
         * @param form 要渲染的form的jquery对象，当form为空默认值为全页面
         * @param sgl 有值单个，无值默认范围    
         */
        function initDateForm(sgl, form) {
            if (isNull(form)) form = $(document.body);
            var ltm = function (tar, tars, tva) {
                tars.each(function () {
                    $(this).removeAttr("lay-key");
                    this.outerHTML = this.outerHTML;
                });
                tars = form.find(".dateTarget" + tar);
                tars.each(function () {
                    var ele = $(this);
                    if ("y" == tva) {
                        laydate.render({
                            elem: this,
                            type: 'year',
                            range: '~',
                            format: 'yyyy年',
                            range: sgl ? null : '~',
                            done: function (value, date, endDate) {
                                if (typeof (date.year) == "number") ele.attr("startDate", date.year);
                                else ele.attr("startDate", "");
                                if (typeof (endDate.year) == "number") ele.attr("endDate", endDate.year);
                                else ele.attr("endDate", "");
                            }
                        });

                    } else if ("s" == tva) {
                        ele.attr("startDate", "");
                        ele.attr("endDate", "");
                        renderSeasonDate(this, sgl);
                    } else if ("m" == tva) {
                        ele.attr("startDate", "");
                        ele.attr("endDate", "");
                        laydate.render({
                            elem: this,
                            type: 'month',
                            range: '~',
                            format: 'yyyy年MM月',
                            range: sgl ? null : '~',
                            done: function (value, date, endDate) {
                                if (typeof (date.month) == "number") ele.attr("startDate", date.year + "-" + date.month);
                                else ele.attr("startDate", "");
                                if (typeof (endDate.month) == "number") ele.attr("endDate", endDate.year + "-" + endDate.month);
                                else ele.attr("endDate", "");
                            }
                        });
                    } else if ("d" == tva) {
                        ele.attr("startDate", "");
                        ele.attr("endDate", "");
                        laydate.render({
                            elem: this,
                            range: '~',
                            format: 'yyyy年MM月dd日',
                            range: sgl ? null : '~',
                            done: function (value, date, endDate) {
                                if (typeof (date.date) == "number") ele.attr("startDate", date.year + "-" + date.month + "-" + date.date);
                                else ele.attr("startDate", "");
                                if (typeof (endDate.date) == "number") ele.attr("endDate", endDate.year + "-" + endDate.month + "-" +
                                    endDate.date);
                                else ele.attr("endDate", "");
                            }
                        });
                    }
                });
            }
            var sels = form.find(".dateSelector");
            sels.each(function (i, e) {
                var ths = this;
                var thiz = $(e);
                var tar = thiz.attr("date-target");
                thiz.next().find("dd").click(function () {
                    var tva = thiz.val();
                    var tars = form.find(".dateTarget" + tar);
                    ltm(tar, tars, tva);
                });
                thiz.change(function () {
                    var tva = $(this).val();
                    var tars = form.find(".dateTarget" + tar);
                    ltm(tar, tars, tva);
                });
                var tars = form.find(".dateTarget" + tar);
                ltm(tar, tars, thiz.val());
            });
        }

        function isNull(s) {
            if (s == null || typeof (s) == "undefined" || s == "") return true;
            return false;
        }
    </script>
eof;

    }

    /**
     * Desc : 编辑页表格操作组件
     * User : you name
     * Date : 2024-03-13 14:03
     * @param string $list
     * @param string $del
     * @param string $copy
     * @param string $add
     * @return string
     */
    public static function tablePperation($list = '', $del = '', $copy = '', $add = '',$edit = ''): string
    {
        $but = '';
        if($list){
            $but .= "<span class='list-number'>".$list."</span>";
        }
        if($copy){
            $but .= "<img class='action-but' title='复制此行' onclick='".$copy."' src='".root."img/control/list_copy.svg'>";
        }
        if($add){
            $but .= "<img class='action-but' title='向下添加一行' onclick=\"".$add."\" src='".root."img/control/list_add.svg'>";
        }
        if($edit){
            $but .= "<img class='action-but' title='编辑' onclick='".$edit."' src='".root."img/control/edit.svg'>";
        }
        if($del){
            $but .= "<img class='action-but' title='删除此行' onclick='".$del."' src='".root."img/control/list_del.svg'>";
        }
        return <<<eof
                    <div class="table-edit-action">                       
                        {$but}
                    </div>
eof;

    }

    public static function monthRange($monthRange): array
    {
        // 使用 explode 函数分割字符串
        $data = explode('~', $monthRange);
        $months = [];
        if (is_array($data)) {
            $startMonth = trim($data[0]);
            $endMonth = trim(end($data));
            $startStamp = strtotime($startMonth.'-01');
            $endStamp   = strtotime($endMonth.'-01');
            if($startStamp && $endStamp && $endStamp >= $startStamp){
                $months[] = $startMonth;
                while ($startMonth != $endMonth) {
                    $startMonth = date("Y-m",strtotime($startMonth."-01 +1 month"));
                    $months[] = $startMonth;
                }
            }
        }

        return $months;
    }

    /**
     * Desc : 上下切换月份
     * User : you name
     * Date : 2024-06-21 11:41
     * @return string
     */
    public function switchMonth(): string
    {
        $type = $this->post('type'); // 切换方向
        $refer = $this->post('month'); // 参考月份
        $firstMonth = $this->post('firstMonth'); // 选择器第一个月份
        $lastMonth = $this->post('lastMonth'); // 选择器最后一个月份
        $timeSlot = $this->post('timeSlot'); // 筛选时间段
        $searchMonth = self::monthRange($timeSlot);

        if($type == 'up'){
            $val = date("Y-m",strtotime($refer."-01 -1 month"));
            $firstMonth = $val;
            $lastMonth = date("Y-m",strtotime($lastMonth."-01 -1 month"));
        }else{
            $val = date("Y-m",strtotime($refer."-01 +1 month"));
            $lastMonth = $val;
            $firstMonth = date("Y-m",strtotime($firstMonth."-01 +1 month"));
        }
        $class = 'month';
        $disable = false;  //不存在凭证数据，禁用
        if(static::total(array('table' => 'voucher', 'where' => "coid = '" . self::$coid . "' and date_format(recordDate,'%Y-%m') = '$val' and isTemplate = '否'")) == 0){
            $disable = true;
        }
        $class .= $disable ? ' grey' : ' black'; //禁用样式
        if(in_array($val, $searchMonth) && !$disable){
            $class .= ' selected';
        }
        $month = (int)date("m",strtotime($val.'-01'));
        $year = (int)date("Y",strtotime($val.'-01'));
        $json['html'] = '';
        if($month == 1){
            $json['html'] .= "<div class='year-separator'>{$year}</div>";
        }
        $json['html'] .= "<div class='$class' data-month='$month' data-year='$year' data-date='$val' onclick='selectedMonth(this)'>{$month}月</div>";

        $json['downDisable'] = $json['upDisable'] = 0;
        // 判断选择器第一个月份之前是否有凭证，没有则不可切换
        if(static::total(array('table' => 'voucher', 'where' => "coid = '" . self::$coid . "' and date_format(recordDate,'%Y-%m') < '$firstMonth' and isTemplate = '否'")) == 0){
            $json['upDisable'] = 1;
        }
        // 选择器最后一个月份大于当前月，判断选择器最后一个月份之后是否有凭证，没有则不可切换
        if($lastMonth >= date("Y-m") && static::total(array('table' => 'voucher', 'where' => "coid = '" . self::$coid . "' and date_format(recordDate,'%Y-%m') > '$lastMonth' and isTemplate = '否'")) == 0){
            $json['downDisable'] = 1;
        }
        return json_encode($json, 320);
    }

    /**
     * Desc : 财务月份选择器
     * User : you name
     * Date : 2024-03-13 14:03
     * @param string $timeSlot 2024-03~2024-05 选中的月份区间
     * @param string $inputName
     * @return array
     */
    public static function monthSelector($timeSlot='', $inputName='createTime'): array
    {
        $range = (new static())->post('month_select_range'); // 月份选择范围
        $timeSlot = $timeSlot ?: date("Y-m").'~'.date("Y-m");
        $searchMonth = self::monthRange($timeSlot);
        $html = '';
        // 月份选择范围
        if(!$range) {
            //往前推11个月
            $beforeMonth = date("Y-m", strtotime(end($searchMonth) . "-01 -11 month"));
            $range = $beforeMonth.'~'.end($searchMonth);
        }
        $selectRange = self::monthRange($range);
        foreach ($selectRange as $val){
            $class = 'month';
            $disable = false;  //不存在凭证数据，禁用
            if(static::total(array('table' => 'voucher', 'where' => "coid = '" . self::$coid . "' and date_format(recordDate,'%Y-%m') = '$val' and isTemplate = '否'")) == 0){
                $disable = true;
            }
            $class .= $disable ? ' grey' : ' black'; //禁用样式
            if(in_array($val, $searchMonth) && !$disable){
                $class .= ' selected';
            }
            $month = (int)date("m",strtotime($val.'-01'));
            $year = (int)date("Y",strtotime($val.'-01'));
            if($month == 1){
                $html .= "<div class='year-separator'>{$year}</div>";
            }
            $html .= "<div class='$class' data-month='$month' data-year='$year' data-date='$val' onclick='selectedMonth(this)'>{$month}月</div>";
        }
        $downDisable = $upDisable = '';
        // 判断选择器第一个月份之前是否有凭证，没有则不可切换
        if(static::total(array('table' => 'voucher', 'where' => "coid = '" . self::$coid . "' and date_format(recordDate,'%Y-%m') < '$selectRange[0]' and isTemplate = '否'")) == 0){
            $upDisable = 'disabled';
        }
        // 选择器最后一个月份大于当前月，判断选择器最后一个月份之后是否有凭证，没有则不可切换
        if(static::total(array('table' => 'voucher', 'where' => "coid = '" . self::$coid . "' and date_format(recordDate,'%Y-%m') > '".end($selectRange)."' and isTemplate = '否'")) == 0){
            $downDisable = 'disabled';
        }
        $array['button'] = "<img class='toggle-button' alt='月份选择器' title='点击隐藏月份选择框' onclick='toggleMonthsContainer(this)' src='".root."img/control/calendar_open.svg'>";
        $array['html'] = <<<eof
                <div class="month-block">
                    <div class="month-selector hide" id="month-selector">
                        <div class="header">
                            <div class="selected-month" id="selected-month">{$searchMonth[0]}</div>
                        </div>
                        <div class="controls">
                            <button id="up-button" $upDisable onclick="switchMonth('up')">▲</button>
                        </div>
                        <div class="months-container" id="months-container">
                            <div class="months" id="months">
                                $html
                            </div>
                        </div>
                        <div class="controls">
                            <button id="down-button" $downDisable onclick="switchMonth('down')">▼</button>
                        </div>
                        <div class="hide" id="time-slot">$timeSlot</div>
                    </div>
                </div>
<script>
function dataProcess(month, range) {
    $('#selected-month').text(month);
    //更新查询结果
    let searchFormId = $('#month-selector').closest("[searchformhtml]").attr('searchformhtml')
    if($("[searchformhtml=" + searchFormId + "]").find("input[name='month_select_range']").length > 0){
        $("[searchformhtml=" + searchFormId + "]").find("input[name='month_select_range']").val(range)
    }else{
        $("[searchformhtml=" + searchFormId + "]").find(".search-hidden").append("<input type='hidden' name='month_select_range' value='"+range+"'>")
    }
    $("[searchformhtml=" + searchFormId + "]").find("input[name='$inputName']").val(month+'~'+month)
    $("[searchformhtml=" + searchFormId + "]").find("input[type=submit]").click();
}
function selectedMonth(obj) {
    if (!$(obj).hasClass('grey')) {
        let monthsEl = $('#months')
        let firstMonth = monthsEl.children('.month').first()
        let lastMonth = monthsEl.children('.month').last()
        let range = firstMonth.attr('data-date') + '~' + lastMonth.attr('data-date')
        $('.month-block .month').removeClass('selected');
        $(obj).addClass('selected');
        dataProcess($(obj).attr('data-date'), range)
    }
}
function switchMonth(type) {
    let monthsEl = $('#months')
    let timeSlot = $('#time-slot')
    let monthsContainer = $('#months-container')
    let upButton = $('#up-button')
    let downButton = $('#down-button')
    let firstMonth = monthsEl.children('.month').first()
    let lastMonth = monthsEl.children('.month').last()
    let referEl = type === 'up' ? firstMonth : lastMonth
    //提交
    $.post(root + "control/adIndex.html?cla=component&fun=switchMonth", {type: type, month: referEl.attr('data-date'), firstMonth: firstMonth.attr('data-date'), lastMonth: lastMonth.attr('data-date'), timeSlot: timeSlot.text()}, function (data) {
        if (data.warn) {
            warn(data.warn);
        } else {
            if(type === 'up'){
                monthsEl.prepend(data.html)
                monthsContainer.scrollTop(0)
                // 删除最后一个monthEl元素
                if(lastMonth.attr('data-month') === '1'){
                    lastMonth.prev('.year-separator').remove()
                }
                lastMonth.remove()
            }else{
                monthsEl.append(data.html);
                monthsContainer.scrollTop(monthsContainer.prop('scrollHeight'))
                // 删除第一个monthEl元素
                if(firstMonth.attr('data-month') === '1'){
                    firstMonth.prev('.year-separator').remove()
                }
                firstMonth.remove()
            }
            upButton.prop("disabled", data.upDisable)
            downButton.prop("disabled", data.downDisable)
        }
    }, "json");
}
function toggleMonthsContainer(e){
    let toggleButton = $(e);
    let monthSelector = $('#month-selector');
    if (monthSelector.hasClass('hide')) {
        toggleButton.attr('title', '点击隐藏月份选择框');
        toggleButton.attr('src', root+'img/control/calendar_open.svg');
    } else {
        toggleButton.attr('title', '点击展开月份选择框');
        toggleButton.attr('src', root+'img/control/calendar_close.svg');
    }
    monthSelector.toggleClass('hide');
}
$(document).ready(function() {
    laydate.render({
        elem: '#selected-month'
        ,theme: 'default'
        ,format: "yyyy-MM"
        ,calendar: true
        ,type: 'month'
        ,value: $('#selected-month').text()
        ,ready: function(date){
            //console.log(date)
        }
        ,done: function(value){
            console.log(value)
            dataProcess(value)
        }
        ,showBottom: false
    });
    toggleMonthsContainer();  // 默认展开
});
</script>
eof;
        return $array;
    }

    /**
     * Desc : 左侧导航结构
     * User : you name
     * Date : 2024-07-08 14:34
     * @param $data
     * @return string
     */
    public static function leftNav($data): string
    {
        $searchvalue = $data['searchvalue'] ?? '';
        $title = $data['title'] ?? '搜索分类';
        $inputOther = $data['inputOther'] ?? '';  // 搜索框 附加参数
        $searchFun = $data['searchFun'] ?? 'search_left';
        $inputOther .= " onkeyup='$searchFun(event)' "; //搜索框keyup事件

        $click = $data['click'] ?? ''; // 全部分类点击方法
        $button = $data['button'] ?? ''; // 操作按钮
        $allTitle = $data['allTitle'] ?? ''; // 全部分类名称
        $topButton = $allTitle ? '' : $button; // 如果没有全部分类的按钮，则把操作按钮放在顶部
        $search          = "<div class='container-left-search-parent flex-box'>
                                ".text([
                                    "name" => 'name',
                                    'title' => $title,
                                    'id' => 'container-left-search',
                                    'class' => 'container-left-search',
                                    'other' => $inputOther,
                                    'value' => $searchvalue
                                ])."
                                <i class='container-left-search-ico' title='查询' onclick=\"$searchFun(event)\"></i>
                                <span class='search-count'>0/0</span>
                                ".$topButton."
                            </div>";
        // 全部分类
        $buttonHtml = '';
        if($allTitle) {
            $buttonHtml = "<div class='container-top-tool'>
                        <ul class='all-type'>
                            <li class='active' data-id='all' onclick=\"{$click}\" title='{$allTitle}'>
                                <img class='menu-img' src='" . root . "img/control/menu.svg'>
                                <span class=''>{$allTitle}</span>
                            </li>
                        </ul>
                        $button
                    </div>";
            $css = <<<EOT
<style>
.container-main .container-left .classify-area {height: calc(100vh - 120px);}
</style>
EOT;
        }else{
            $css = <<<EOT
<style>
.container-main .container-left .classify-area {height: calc(100vh - 90px);}
</style>
EOT;
        }
        // 加上顶部操作按钮，需要调整搜索框宽度
        if($topButton){
            $css .= <<<EOT
<style>
.container-main .container-left-search {max-width: 120px;}
</style>
EOT;
        }
        $js = '';
        // 初始化
        if($searchvalue){
            $js .= <<<EOT
<script>
$(document).ready(function(){
    //初始化左侧分类标记和定位
    search_left(event)
});
</script>
EOT;
        }
        // 显示隐藏操作按钮
        if(!empty($data['edit']??'')){
            $js .= <<<EOT
<script>
$('.container-left').on('mouseenter', 'li[data-id]', function(event) {
    $(this).find(".actionBlock").removeClass('none');
})
// 移出二级菜单和三级菜单区域
$(document).on('mouseleave', 'li[data-id]', function(event) {
    $(".actionBlock").addClass('none')
});
</script>
EOT;
        }
        $all = $data['all'] ?? '';
        $rightHtml = $data['rightHtml'] ?? '';
        $html =  <<<EOT
        <div class="container-main">
            <div class="container-left" id="container-left">
                {$search}
                {$buttonHtml}
                <div class="classify-area">
                    {$all}
                </div>
            </div>
            <div class="container-right" id="container-detail">
               {$rightHtml}
            </div>
        </div>
EOT;
        return $html.$js.$css;
    }

}
